﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.Threading;
using System.Timers;
using System.Web;
using System.Xml;
using umbraco.cms.businesslogic.member;
using umbraco.DataLayer;
using Timer = System.Timers.Timer;
using System.Web.Hosting;


namespace Designit.Umb.Newsletter.Bll
{
    public class BounceHandling : IHttpModule
    {
        private static string text = "";
        private static NetworkStream netStream;
        private static byte[] WriteBuffer;
        private Timer timer;
        private static bool HasAppStarted;
        private readonly static object _syncObject = new object();
        public void Init(HttpApplication context)
        {
            //workaround to avoid 3 x call 
            if (!HasAppStarted)
            {

                lock (_syncObject)
                {
                    if (!HasAppStarted)
                    {
                        timer = new Timer(60000);
                        timer.Elapsed += timer_Elapsed;
                        timer.Start();
                        HasAppStarted = true;

                    }
                }
            }
        }

        void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                timer.Stop();
                Run();
            }
            finally
            {
                timer.Start();
            }
        }



        public void Run()
        {


            XmlDocument doc = new XmlDocument();
            string path = @"/config/designit/newsletter/mailSettings.config";
            path = HostingEnvironment.MapPath(path);
            doc.Load(path);
            XmlNode xnode = doc.SelectSingleNode("//mailSettings/mailSetting[@name='Bounce']");
            string checkinterval = xnode.Attributes[1].Value;
            xnode = doc.SelectSingleNode("//mailSettings/mailSetting[@name='LastCheck']");
            string lastCheck = xnode.Attributes[1].Value;
            bool check = false;
            DateTime last;
            if (string.IsNullOrEmpty(lastCheck))
            {
                CheckMail();
                xnode = doc.SelectSingleNode("//mailSettings/mailSetting[@name='LastCheck']");
                xnode.Attributes[1].Value = DateTime.Now.ToString();
                check = true;
                doc.Save(path);
            }
            else
            {
                last = DateTime.Parse(lastCheck);
                if (last.AddHours(Int32.Parse(checkinterval)) < DateTime.Now)
                {
                    CheckMail();
                    xnode = doc.SelectSingleNode("//mailSettings/mailSetting[@name='LastCheck']");
                    xnode.Attributes[1].Value = DateTime.Now.ToString();
                    check = true;
                    doc.Save(path);
                }
            }
            if (check)
            {
                timer.Interval = (Int32.Parse(checkinterval) * 1000 * 60 * 60);
            }
            else
            {
                last = DateTime.Parse(lastCheck);
                TimeSpan sleep = last.AddHours(Int32.Parse(checkinterval)) - DateTime.Now;
                timer.Interval = sleep.TotalMilliseconds;
            }




        }

        private static void CheckMail()
        {
            //read mail settingsinfo
            XmlDocument docSetting = new XmlDocument();
            string path = @"/config/designit/newsletter/mailSettings.config";
            path = HostingEnvironment.MapPath(path);
            docSetting.Load(path);
            XmlNode xnode = docSetting.SelectSingleNode("//mailSettings/mailSetting[@name='POP3']");
            string pop3 = xnode.Attributes[1].Value;
            xnode = docSetting.SelectSingleNode("//mailSettings/mailSetting[@name='POP3port']");
            string pop3Port = xnode.Attributes[1].Value;
            xnode = docSetting.SelectSingleNode("//mailSettings/mailSetting[@name='UserId']");
            string user = xnode.Attributes[1].Value;
            xnode = docSetting.SelectSingleNode("//mailSettings/mailSetting[@name='Password']");
            string pass = xnode.Attributes[1].Value;
            xnode = docSetting.SelectSingleNode("//mailSettings/mailSetting[@name='SoftBounce']");
            string softbounce = xnode.Attributes[1].Value;

            // Create a TCP client for a TCP connection

            TcpClient tcpClient = new TcpClient();

            // Connect this TCP client to the server IP/name and port specified in the form

            tcpClient.Connect(pop3, Convert.ToInt32(pop3Port));

            // Create a network stream to retrieve data from the TCP client

            netStream = tcpClient.GetStream();

            // Create a stream reader to be able to read the network stream

            using (var strReader = new System.IO.StreamReader(netStream))
            {

                if (tcpClient.Connected)
                {

                    strReader.ReadLine();
                    // Pass the username to the server
                    issue_command("USER " + user);
                    strReader.ReadLine();

                    // Pass the password to the server
                    issue_command("PASS " + pass);
                    strReader.ReadLine();

                    // Now that we are (probably) authenticated, list the messages

                    issue_command("LIST");
                    strReader.ReadLine();

                    string ListMessage;
                    bool last = false;
                    string regex = "";
                    string mailId = "";
                    bool deleted = false;

                    //retrieve all mail id's
                    while (!last)
                    {

                        ListMessage = strReader.ReadLine();

                        if (ListMessage == ".")
                        {

                            last = true;

                        }

                        else
                        {
                            regex = @"([\w]+\s+)";
                            mailId += Regex.Match(ListMessage, regex, RegexOptions.IgnoreCase) + " ";

                        }

                    }

                    if (!string.IsNullOrEmpty(mailId))
                    {
                        //retrieve mails given mail id
                        regex = @"([\w]+\s+)";

                        foreach (var match in Regex.Matches(mailId, regex))
                        {
                            StringBuilder sbuilder = new StringBuilder();
                            issue_command("RETR " + match.ToString());
                            last = false;
                            //retrieve all mail id's
                            while (!last)
                            {

                                ListMessage = strReader.ReadLine();

                                if (ListMessage == ".")
                                {

                                    last = true;

                                }

                                else
                                {
                                    sbuilder.AppendLine(ListMessage);

                                }

                            }
                            //search text for reciever
                            regex = @"RCPT(.*?)\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b";
                            string reciever =
                                Regex.Match(sbuilder.ToString(), regex, RegexOptions.IgnoreCase).ToString();
                            if (!string.IsNullOrEmpty(reciever))
                            {
                                regex = @"\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b";
                                //isolate reciever in string
                                reciever = Regex.Match(reciever, regex, RegexOptions.IgnoreCase).ToString();

                                string domain = reciever.Split('@')[1];
                                XmlDocument docBounce = new XmlDocument();
                                path = @"/config/designit/newsletter/bounceHandling.config";
                                path = HostingEnvironment.MapPath(path);
                                docBounce.Load(path);

                                regex = "";
                                //loads regex from xml given domain
                                xnode =
                                    docBounce.SelectSingleNode("//bounces/hardbounces/hardbounce[@name='" + domain +
                                                               "']");
                                if (xnode != null)
                                {
                                    regex = xnode.Attributes["regex"].Value;
                                }
                                else
                                {
                                    domain = domain.Substring(0, domain.LastIndexOf('.')) + ".*";
                                    xnode =
                                        docBounce.SelectSingleNode("//bounces/hardbounces/hardbounce[@name='" + domain +
                                                                   "']");
                                    if (xnode != null)
                                    {
                                        regex = xnode.Attributes["regex"].Value;
                                    }
                                    else
                                    {
                                        domain = "*";
                                        xnode =
                                            docBounce.SelectSingleNode("//bounces/hardbounces/hardbounce[@name='" +
                                                                       domain + "']");
                                        if (xnode != null)
                                        {
                                            regex = xnode.Attributes["regex"].Value;
                                        }
                                    }
                                }

                                if (!string.IsNullOrEmpty(regex))
                                {
                                    if (Regex.IsMatch(sbuilder.ToString(), regex))
                                    {
                                        //add member to bounced group

                                        Member member = Member.GetMemberFromEmail(reciever);
                                        if (member != null)
                                        {
                                            member.AddGroup(MemberGroup.GetByName("Bounced").Id);
                                        }
                                        //delete mail
                                        issue_command("DELE " + match.ToString());
                                        strReader.ReadLine();

                                        deleted = true;
                                    }
                                }

                                regex = "";

                                //check for softbounces
                                if (!deleted && softbounce == "true")
                                {
                                    //loads regex from xml given domain
                                    xnode =
                                        docBounce.SelectSingleNode("//bounces/softbounces/softbounce[@name='" + domain +
                                                                   "']");
                                    if (xnode != null)
                                    {
                                        regex = xnode.Attributes["regex"].Value;
                                    }
                                    else
                                    {
                                        if (domain != "*")
                                        {
                                            domain = domain.Substring(0, domain.LastIndexOf('.')) + "*";
                                        }
                                        xnode =
                                            docBounce.SelectSingleNode("//bounces/softbounces/softbounce[@name='" +
                                                                       domain + "']");
                                        if (xnode != null)
                                        {
                                            regex = xnode.Attributes["regex"].Value;
                                        }
                                    }


                                }

                                if (!string.IsNullOrEmpty(regex))
                                {



                                    Member member = Member.GetMemberFromEmail(reciever);
                                    if (member != null)
                                    {
                                        //check if member is allready marked
                                        bool fullmark = false;

                                        int marks = umbraco.BusinessLogic.Application.SqlHelper.ExecuteScalar<int>(
                                            "select bounces from newsLetterSoftBounces where memberId=@memberId",
                                            umbraco.BusinessLogic.Application.SqlHelper.CreateParameter("@memberId",
                                                                                                        member.Id));

                                        if (marks > 1)
                                        {
                                            fullmark = true;
                                        }


                                        if (fullmark)
                                        {
                                            //add member to bounced group if needed
                                            member.AddGroup(MemberGroup.GetByName("Bounced").Id);
                                        }
                                        else
                                        {
                                            //mark member or update members marks 
                                            if (marks > 0)
                                            {
                                                marks++;
                                                umbraco.BusinessLogic.Application.SqlHelper.ExecuteNonQuery(
                                                    "update newsLetterSoftBounces set bounces=@bounces where memberId=@memberId",
                                                    umbraco.BusinessLogic.Application.SqlHelper.CreateParameter(
                                                        "@bounces", marks),
                                                    umbraco.BusinessLogic.Application.SqlHelper.CreateParameter(
                                                        "@memberId", member.Id));
                                            }
                                            else
                                            {
                                                marks = 1;
                                                umbraco.BusinessLogic.Application.SqlHelper.ExecuteNonQuery(
                                                    "insert into newsLetterSoftBounces (memberId, bounces) values(@memberId, @bounces)",
                                                    umbraco.BusinessLogic.Application.SqlHelper.CreateParameter(
                                                        "@memberId", member.Id),
                                                    umbraco.BusinessLogic.Application.SqlHelper.CreateParameter(
                                                        "@bounces", marks));
                                            }

                                        }




                                    }


                                    //delete mail
                                    issue_command("DELE " + match.ToString());
                                    strReader.ReadLine();
                                    deleted = true;
                                }
                            }
                            else
                            {
                                //delete mail
                                issue_command("DELE " + match.ToString());
                                strReader.ReadLine();
                                deleted = true;
                            }

                        }
                    }


                    //close the connection thus saving changes
                    issue_command("QUIT");
                }
            }
            netStream.Close();
        }

        private static void issue_command(string command)
        {
            //send the command to the pop server
            text = command + "\r\n";
            WriteBuffer = System.Text.Encoding.ASCII.GetBytes(text.ToCharArray());
            netStream.Write(WriteBuffer, 0, WriteBuffer.Length);

        }

        public void Dispose()
        {

        }
    }
}
